/* 
 * File:   processing.cpp
 * Author: Elias Capito, Jonathan Neuhaus
 * 
 * Created on 6. mai 2010, 15:12
 */

#include "processing.h"
#include "utils.h"

//Element structurant pour les opérations morphologiques
IplConvKernel* Processing::elm3x3Rect = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_RECT);

//Décompose une image couleur en HSV
//Retourne un tableau avec [0] = hue, [1] = sat, [2] = bri
IplImage** Processing::getHSV(IplImage* image) {
    IplImage* hsvStack = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, image->nChannels);
    IplImage* hue = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, CV_8UC1);
    IplImage* sat = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, CV_8UC1);
    IplImage* bri = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, CV_8UC1);

    // Décomposition HSV
    cvCvtColor(image, hsvStack, CV_BGR2HSV);

    // Récupération du canal hue uniquement
    cvSplit(hsvStack, hue, sat, bri, NULL);

    IplImage * stack[3] = {hue, sat, bri};
    return stack;
}

//Egalise l'histogramme d'une image en niveau de gris
IplImage* Processing::equalizeHistGray(IplImage* image) {
    IplImage* tmp = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, CV_8UC1);

    cvEqualizeHist(image, tmp);
    return tmp;
}

//Effectue un seuillage avec val min et max
IplImage* Processing::thresholdImgGray(IplImage* image, int min, int max, int type) {
    IplImage* tmp = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);

    cvThreshold(image, tmp, min, max, type);
    return tmp;
}

//Effectue une inversion sur l'image  noir->blanc, blanc->noir
IplImage* Processing::invertImgGray(IplImage* image) {
    IplImage *tmp = cvCreateImage(cvSize(image->width, image->height), IPL_DEPTH_8U, CV_8UC1);

    cvNot(image, tmp);
    return tmp;
}

//Opération morphologie erosion en utilisant l'élément structurant 3x3
IplImage* Processing::morphErodeImg(IplImage* image, int iteration) {
    IplImage *tmp = cvCreateImage(cvSize(image->width, image->height), IPL_DEPTH_8U, CV_8UC1);

    cvErode(image, tmp, elm3x3Rect, iteration);
    return tmp;
}

//Opération morphologie dilatation en utilisant l'élément structurant 3x3
IplImage* Processing::morphDilateImg(IplImage* image, int iteration) {
    IplImage *tmp = cvCreateImage(cvSize(image->width, image->height), IPL_DEPTH_8U, CV_8UC1);

    cvDilate(image, tmp, elm3x3Rect, iteration);
    return tmp;
}

//Opération morphologie ouverture en utilisant l'élément structurant 3x3
IplImage* Processing::morphOpenImg(IplImage* image, int iteration) {
    IplImage *tmp;

    tmp = morphErodeImg(image, iteration);
    tmp = morphDilateImg(tmp, iteration);
    return tmp;
}

//Opération morphologie fermeture en utilisant l'élément structurant 3x3
IplImage* Processing::morphCloseImg(IplImage* image, int iteration) {
    IplImage *tmp;

    tmp = morphDilateImg(image, iteration);
    tmp = morphErodeImg(tmp, iteration);
    return tmp;
}

//Soustraction d'une image
IplImage* Processing::subImgGray(IplImage* image, IplImage* sub) {
    IplImage *tmp = cvCreateImage(cvSize(image->width, image->height), IPL_DEPTH_8U, CV_8UC1);

    cvSub(image, sub, tmp, NULL);
    return tmp;
}

//Detection d'un cercle sur une image en niveau de gris
float* Processing::detectCircleGray(IplImage* image, int minRadius, int maxRadius) {
    IplImage *tmp = cvCreateImage(cvSize(image->width, image->height), IPL_DEPTH_8U, CV_8UC1);
    CvMemStorage* storage = cvCreateMemStorage(0);
    //     //cvCvtColor(src, tmp, CV_BGR2GRAY);
    cvSmooth(image, tmp, CV_GAUSSIAN, 9, 9);
    CvSeq* circles = cvHoughCircles(tmp, storage, CV_HOUGH_GRADIENT, 1, 2000, 100, 15, minRadius, maxRadius);

    return (float*) cvGetSeqElem(circles, 0);
}

//Permet de rogner une image
IplImage* Processing::crop(IplImage* image, int centerX, int centerY, int diameter) {
    int radius = diameter / 2;
    IplImage *dst = cvCreateImage(cvSize(2 * radius, 2 * radius), image->depth, image->nChannels);

    cvSetImageROI(image, cvRect(centerX - radius, centerY - radius, radius * 2, radius * 2));
    cvCopy(image, dst, NULL);
    cvResetImageROI(image);
    return dst;
}

//Recherche tous les pixels sur un rayon donné à partir du centre
//Retourn un tableau de 3600, qui contient les pixels
int* Processing::getTabNeedles(IplImage* image, int centerX, int centerY, int radius) {
    int *arrayBW = new int[3600];
    radius /= 2;
    centerX = radius;
    centerY = radius;
    for (int theta = 0; theta < 3600; theta++) {
        int x = (int) floor(centerX + radius * cos(theta * M_PI / 1800.0 - M_PI / 2.0));
        int y = (int) floor(centerY + radius * sin(theta * M_PI / 1800.0 - M_PI / 2.0));
        if (x >= radius * 2) {
            x = radius * 2 - 1;
        }
        if (y >= radius * 2) {
            y = radius * 2 - 1;
        }

        //Récupération de la valeur dans un tableau transposé
        arrayBW[theta] = ((uchar *) (image->imageData + y * image->widthStep))[x];
    }

    return arrayBW;
}

//Recherche le centre et la largeur des aiguilles sur le tableau de 3600
//Retourne un tableau [2] avec [0] = heures, [1] = minutes
int* Processing::getTime(int* needleTab) {
    int i = 0, cpt = 0, start = 0, end = 0;
    int tab[2][2];

    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            tab[i][j] = 0;

        }
    }
    // Find the needles
    while (i < 3600) {
        if (needleTab[i] == 0) {
            start = i;
            while (needleTab[i] == 0) {
                i++;
            }
            end = i - 1;
            if (cpt < 2) {
                tab[cpt][0] = (start + end) / 2; // moyenne
                tab[cpt][1] = end - start; // largeur`
                cpt++;
            }
        }
        i++;
    }

    int needle[2];

    //S'il n'y a que une aiguille trouvée
    if (cpt == 1) {
        needle[0] = tab[0][0];
        needle[1] = needle[0]; // heures = minutes
    } else {
        //Qu'elle est la plus grande en largeur?
        if (tab[0][1] > tab[1][1]) {
            needle[0] = tab[0][0]; // heures
            needle[1] = tab[1][0]; // minutes
        } else {
            needle[0] = tab[1][0]; // heures
            needle[1] = tab[0][0]; // minutes
        }
    }

    int *tmpTab = new int[2];
    tmpTab[0] = needle[0] / 300;
    tmpTab[1] = needle[1] / 60;
    return tmpTab;
}